package cn.iocoder.yudao.im.service.Impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.security.core.LoginUser;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.im.client.IMClient;
import cn.iocoder.yudao.im.common.model.IMGroupMessage;
import cn.iocoder.yudao.im.common.model.IMUserInfo;
import cn.iocoder.yudao.im.common.util.CommaTextUtils;
import cn.iocoder.yudao.im.contant.Constant;
import cn.iocoder.yudao.im.contant.RedisKey;
import cn.iocoder.yudao.im.domain.NoticeImGroup;
import cn.iocoder.yudao.im.domain.NoticeImGroupMember;
import cn.iocoder.yudao.im.domain.NoticeImGroupMessage;
import cn.iocoder.yudao.im.domain.moudle.GroupInviteVO;
import cn.iocoder.yudao.im.enums.MessageStatus;
import cn.iocoder.yudao.im.enums.MessageType;
import cn.iocoder.yudao.im.mapper.NoticeImGroupMapper;
import cn.iocoder.yudao.im.service.NoticeImGroupMemberService;
import cn.iocoder.yudao.im.service.NoticeImGroupMessageService;
import cn.iocoder.yudao.im.service.NoticeImGroupService;
import cn.iocoder.yudao.im.util.BeanUtils;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 群 服务实现类
 * </p>
 *
 * @author ZCC
 * @since 2024-07-02
 */
@Service
@Slf4j
public class NoticeImGroupServiceImpl extends ServiceImpl<NoticeImGroupMapper, NoticeImGroup> implements NoticeImGroupService {

    @Resource
    private NoticeImGroupMemberService groupMemberService;

    @Resource
    private NoticeImGroupMessageService groupMessageService;


    @Resource
    private IMClient imClient;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Resource
    private AdminUserService userService;


    @Override
    public NoticeImGroup createGroup(NoticeImGroup vo) {
        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();

        // 保存群组数据
        vo.setOwnerId(loginUser.getId());
        this.save(vo);
        // 把群主加入群
        NoticeImGroupMember groupMember = new NoticeImGroupMember();
        groupMember.setGroupId(vo.getId());
        groupMember.setUserId(vo.getOwnerId());
        groupMember.setHeadImage(vo.getHeadImageThumb());
        groupMember.setAliasName(StringUtils.isEmpty(vo.getAliasName()) ? SecurityFrameworkUtils.getLoginUserNickname() : vo.getAliasName());
        groupMember.setRemark(StringUtils.isEmpty(vo.getRemark()) ? vo.getName() : vo.getRemark());
        groupMemberService.save(groupMember);

        vo.setId(vo.getId());
        vo.setAliasName(groupMember.getAliasName());
        vo.setRemark(groupMember.getRemark());
        log.info("创建群聊，群聊id:{},群聊名称:{}", vo.getId(), vo.getName());
        return vo;
    }

    //    @CacheEvict(value = "#vo.getId()")
    @CacheEvict(cacheNames = "groupId", key = "#vo.getId()")
    @Transactional(rollbackFor = Exception.class)
    @Override
    public NoticeImGroup modifyGroup(NoticeImGroup vo) {
        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        // 校验是不是群主，只有群主能改信息
        NoticeImGroup group = this.baseMapper.selectById(vo.getId());
        // 群主有权修改群基本信息
        if (group.getOwnerId().equals(loginUser.getId())) {
            group = BeanUtils.copyProperties(vo, NoticeImGroup.class);
            this.updateById(vo);
        }
        // 更新成员信息
        NoticeImGroupMember member = groupMemberService.findByGroupAndUserId(vo.getId(), loginUser.getId());
        if (Objects.isNull(member) || member.getQuit()) {
            throw new ServiceException(500, "您不是群聊的成员");
        }
        member.setAliasName(StringUtils.isEmpty(vo.getAliasName()) ? SecurityFrameworkUtils.getLoginUserNickname() : vo.getAliasName());
        member.setRemark(StringUtils.isEmpty(vo.getRemark()) ? Objects.requireNonNull(group).getName() : vo.getRemark());
        groupMemberService.updateById(member);
        log.info("修改群聊，群聊id:{},群聊名称:{}", group == null ? null : group.getId(), group == null ? null : group.getName());
        return vo;
    }

    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = "#groupId")
    @Override
    public void deleteGroup(Long groupId) {
        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        NoticeImGroup group = this.baseMapper.selectById(groupId);
        if (!group.getOwnerId().equals(loginUser.getId())) {
            throw new ServiceException(500, "只有群主才有权限解除群聊");
        }
        // 群聊用户id
        List<Long> userIds = groupMemberService.findUserIdsByGroupId(groupId);
        // 逻辑删除群数据
        group.setDeleted(true);
        this.updateById(group);
        // 删除成员数据
        groupMemberService.removeByGroupId(groupId);
        // 清理已读缓存
        String key = StrUtil.join(":", RedisKey.IM_GROUP_READED_POSITION, groupId);
        redisTemplate.delete(key);
        // 推送解散群聊提示
        this.sendTipMessage(groupId, userIds, String.format("'%s'解散了群聊", SecurityFrameworkUtils.getLoginUserNickname()));
        log.info("删除群聊，群聊id:{},群聊名称:{}", group.getId(), group.getName());
    }

    @Override
    public void quitGroup(Long groupId) {
        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        Long userId = loginUser.getId();
        NoticeImGroup group = this.baseMapper.selectById(groupId);
        if (group.getOwnerId().equals(userId)) {
            throw new ServiceException(500, "您是群主，不可退出群聊");
        }
        // 删除群聊成员
        groupMemberService.removeByGroupAndUserId(groupId, userId);
        // 清理已读缓存
        String key = StrUtil.join(":", RedisKey.IM_GROUP_READED_POSITION, groupId);
        redisTemplate.opsForHash().delete(key, userId.toString());
        // 推送退出群聊提示
        this.sendTipMessage(groupId, Collections.singletonList(userId), "您已退出群聊");
        log.info("退出群聊，群聊id:{},群聊名称:{},用户id:{}", group.getId(), group.getName(), userId);
    }

    @Override
    public void kickGroup(Long groupId, String userIds) {
        String[] split = userIds.split(",");
        List<String> list = Arrays.asList(split);
        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        NoticeImGroup group = this.baseMapper.selectById(groupId);
        if (!group.getOwnerId().equals(loginUser.getId())) {
            throw new ServiceException(500, "您不是群主，没有权限踢人");
        }
        if (list.contains(group.getOwnerId().toString())) {
            throw new ServiceException(500, "亲，不能移除自己哟");
        }
        for (String userId : list) {
            // 删除群聊成员
            groupMemberService.removeByGroupAndUserId(groupId, Long.parseLong(userId));
            // 清理已读缓存
            String key = StrUtil.join(":", RedisKey.IM_GROUP_READED_POSITION, groupId);
            redisTemplate.opsForHash().delete(key, userId);
            // 推送踢出群聊提示
            this.sendTipMessage(groupId, Collections.singletonList(Long.parseLong(userId)), "您已被移出群聊");
            log.info("踢出群聊，群聊id:{},群聊名称:{},用户id:{}", group.getId(), group.getName(), userId);
        }
    }

    @Override
    public NoticeImGroup findById(Long groupId) {

        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        NoticeImGroup group = super.getById(groupId);
        if (Objects.isNull(group)) {
            throw new ServiceException(500, "群组不存在");
        }
        NoticeImGroupMember member = groupMemberService.findByGroupAndUserId(groupId, loginUser.getId());
        if (Objects.isNull(member)) {
            throw new ServiceException(500, "您未加入群聊");
        }
        long count = groupMemberService.count(new LambdaQueryWrapper<NoticeImGroupMember>().eq(NoticeImGroupMember::getGroupId, groupId).eq(NoticeImGroupMember::getQuit, false));
        group.setMemberCount(count);
        group.setAliasName(member.getAliasName());
        group.setRemark(member.getRemark());
        group.setQuit(member.getQuit());
        return group;
    }

    @Cacheable(value = "#groupId")
    @Override
    public NoticeImGroup getById(Long groupId) {
        NoticeImGroup group = super.getById(groupId);
        if (Objects.isNull(group)) {
            throw new ServiceException(500, "群组不存在");
        }
        if (group.getDeleted()) {
            throw new ServiceException(500, "群组'" + group.getName() + "'已解散");
        }
        return group;
    }

    @Override
    public List<NoticeImGroup> findGroups() {
        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        // 查询当前用户的群id列表
        List<NoticeImGroupMember> groupMembers = groupMemberService.findByUserId(loginUser.getId());
//        // 一个月内退的群可能存在退群前的离线消息,一并返回作为前端缓存
//        groupMembers.addAll(groupMemberService.findQuitInMonth(loginUser.getId()));
        if (groupMembers.isEmpty()) {
            return new LinkedList<>();
        }
        // 拉取群列表
        List<Long> ids = groupMembers.stream().map((NoticeImGroupMember::getGroupId)).collect(Collectors.toList());
        LambdaQueryWrapper<NoticeImGroup> groupWrapper = Wrappers.lambdaQuery();
        groupWrapper.in(NoticeImGroup::getId, ids);
        List<NoticeImGroup> list = this.list(groupWrapper);
        for (NoticeImGroup noticeImGroup : list) {
            long count = groupMemberService.count(new LambdaQueryWrapper<NoticeImGroupMember>().eq(NoticeImGroupMember::getGroupId, noticeImGroup.getId()).eq(NoticeImGroupMember::getQuit, false));
            noticeImGroup.setMemberCount(count);
        }
        return list;
    }

    @Override
    public void invite(GroupInviteVO vo) {
        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        NoticeImGroup group = this.baseMapper.selectById(vo.getGroupId());
        if (Objects.isNull(group)) {
            throw new ServiceException(500, "群聊不存在");
        }
        NoticeImGroupMember member = groupMemberService.findByGroupAndUserId(vo.getGroupId(), loginUser.getId());
        if (member.getQuit()) {
            throw new ServiceException(500, "您不在群聊中,邀请失败");
        }
        // 群聊人数校验
        List<NoticeImGroupMember> members = groupMemberService.findByGroupId(vo.getGroupId());
        long size = members.stream().filter(m -> !m.getQuit()).count();
        if (vo.getUserIds().size() + size > Constant.MAX_GROUP_MEMBER) {
            throw new ServiceException(500, "群聊人数不能大于" + Constant.MAX_GROUP_MEMBER + "人");
        }
        // 找出好友信息
        List<AdminUserDO> userList = new ArrayList<>();
        for (Long id : vo.getUserIds()) {
            AdminUserDO user = userService.getUser(id);
            userList.add(user);
        }

        // 批量保存成员数据
        List<NoticeImGroupMember> groupMembers = userList.stream().map(f -> {
            Optional<NoticeImGroupMember> optional = members.stream().filter(m -> m.getUserId().equals(f.getId())).findFirst();
            NoticeImGroupMember groupMember = optional.orElseGet(NoticeImGroupMember::new);
            groupMember.setGroupId(vo.getGroupId());
            groupMember.setUserId(f.getId());
            groupMember.setAliasName(f.getUsername());
            groupMember.setRemark(group.getName());
            groupMember.setHeadImage(f.getAvatar());
            groupMember.setCreatedTime(LocalDateTime.now());
            groupMember.setQuit(false);
            return groupMember;
        }).collect(Collectors.toList());
        if (!groupMembers.isEmpty()) {
            groupMemberService.saveOrUpdateBatch(groupMembers);
        }
        // 推送进入群聊消息
        List<Long> userIds = groupMemberService.findUserIdsByGroupId(vo.getGroupId());
        String memberNames = groupMembers.stream().map(NoticeImGroupMember::getAliasName).collect(Collectors.joining(","));
        String content = String.format("'%s'邀请'%s'加入了群聊", SecurityFrameworkUtils.getLoginUserNickname(), memberNames);
        this.sendTipMessage(vo.getGroupId(), userIds, content);
        log.info("邀请进入群聊，群聊id:{},群聊名称:{},被邀请用户id:{}", group.getId(), group.getName(), vo.getUserIds());
    }

    @Override
    public List<NoticeImGroupMember> findGroupMembers(Long groupId) {
        List<NoticeImGroupMember> members = groupMemberService.findByGroupId(groupId);
        List<Long> userIds = members.stream().map(NoticeImGroupMember::getUserId).collect(Collectors.toList());
        List<Long> onlineUserIds = imClient.getOnlineUser(userIds);
        List<NoticeImGroupMember> list = new ArrayList<>();
        for (NoticeImGroupMember member : members) {
            member.setOnline(onlineUserIds.contains(member.getUserId()));
            list.add(member);
        }
        list.sort((m1, m2) -> m2.getOnline().compareTo(m1.getOnline()));
        return list;
    }

    private void sendTipMessage(Long groupId, List<Long> receiveIds, String content) {
        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        // 消息入库
        NoticeImGroupMessage message = new NoticeImGroupMessage();
        message.setContent(content);
        message.setType(MessageType.TIP_TEXT.code());
        message.setStatus(MessageStatus.UNSEND.code());
        message.setSendTime(LocalDateTime.now());
        message.setSendNickName(SecurityFrameworkUtils.getLoginUserNickname());
        message.setGroupId(groupId);
        message.setSendId(loginUser.getId());
        message.setReceiveIds(CommaTextUtils.asText(receiveIds));
        groupMessageService.save(message);
        // 推送
        IMGroupMessage<NoticeImGroupMessage> sendMessage = new IMGroupMessage<>();
        sendMessage.setSender(new IMUserInfo(loginUser.getId(), 0));
        if (CollUtil.isEmpty(receiveIds)) {
            // 为空表示向全体发送
            List<Long> userIds = groupMemberService.findUserIdsByGroupId(groupId);
            sendMessage.setRecvIds(userIds);
        } else {
            sendMessage.setRecvIds(receiveIds);
        }
        sendMessage.setData(message);
        sendMessage.setSendResult(false);
        sendMessage.setSendToSelf(false);
        imClient.sendGroupMessage(sendMessage);
    }
}
